﻿namespace Pibby.Core.IO
{
	using System;
	using System.ComponentModel;
	using System.IO;
	using System.Runtime.InteropServices;
	using System.Security.Permissions;

    public class SimpleFileCopier : ICopyFiles
	{
		#region Events
		
		public event EventHandler<ProgressEventArgs> CopyProgressChanged;

		public event EventHandler CopyCompleted;

		#endregion

		#region Constructors

		public SimpleFileCopier() 
		{
		}

		#endregion

		#region Methods

		public void Copy(string source, string target)
		{
			if (string.IsNullOrEmpty(source))
			{
				throw new ArgumentNullException("source", "Source cannot be blank"); // Resources.ICopyFiles_SourceBlankError);
			}

			if (string.IsNullOrEmpty(target))
			{
				throw new ArgumentNullException("target", "Target cannot be blank"); // Resources.ICopyFiles_TargetBlankError);
			}

			Contract.EnsureThat(!source.Equals(target, StringComparison.CurrentCultureIgnoreCase), "Cannot copy over self");

			string targetFolder = target.Replace(Path.GetFileName(target), string.Empty);

			if (!Directory.Exists(targetFolder))
			{
				Directory.CreateDirectory(targetFolder);
			}
	
			if (File.Exists(source))
			{
				var copycallback = new CopyProgressRoutine(this.CopyProgress);

				new FileIOPermission(FileIOPermissionAccess.Read, source).Demand();
				new FileIOPermission(FileIOPermissionAccess.Write, target).Demand();

				bool cancel = false;
				int restartable = 0x2;

				if (!NativeMethods.CopyFileEx(
									source, 
									target, copycallback,
									IntPtr.Zero, ref cancel, restartable))
				{
					throw new IOException(new Win32Exception().Message);
				}
			}

			EventHandler complete = this.CopyCompleted;

			if (complete != null)
			{
				complete(this, EventArgs.Empty);
			}
		}

		#endregion

		#region Private Methods

		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "As intended")]
		private int CopyProgress(long totalFileSize, long totalBytesTransferred, long streamSize, long streamBytesTransferred, int streamNumber, int callbackReason,
						IntPtr sourceFile, IntPtr destinationFile, IntPtr data)
		{
			try
			{
				EventHandler<ProgressEventArgs> progress = this.CopyProgressChanged;

				if (progress != null)
				{
					//progress(this, new ProgressEventArgs(totalBytesTransferred, totalFileSize, null));
				}

				return 0;
			}
			catch
			{
				// so we don't blow up native method.
				return 0;
			}
		}

		private delegate int CopyProgressRoutine(
								long TotalFileSize, 
								long TotalBytesTransferred, 
								long StreamSize,
								long StreamBytesTransferred, 
								int StreamNumber, 
								int CallbackReason,
								IntPtr SourceFile, 
								IntPtr DestinationFile, 
								IntPtr Data);

		#region Native Win32 API

		/// <summary>
		/// WinAPI functions
		/// </summary>        
		private static class NativeMethods
		{
			[DllImport("Kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
			public static extern bool CopyFileEx(string lpExistingFileName, string lpNewFileName,
													CopyProgressRoutine lpProgressRoutine,
													IntPtr lpData, ref bool pbCancel, int dwCopyFlags);
		}
		#endregion

		#endregion
	}
}
